package com.cmsz.cashier.controller;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Base64;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mobile.device.site.SitePreference;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import com.cmsz.cashier.bean.CashierOrderInoBean;
import com.cmsz.cashier.utils.CommonConstants;
import com.cmsz.cashier.utils.PaymentTypesConstant;
import com.cmsz.cashier.utils.ParamUtil;
import com.cmsz.order.remote.BetterRemoteService;
import com.cmsz.order.remote.SignResult;
import com.cmsz.framework.exception.UpayBusinessException;
import com.cmsz.framework.log.LogHandler;
import com.cmsz.framework.params.entrance.CacheAccessable;
import com.cmsz.framework.params.model.UpayOrgPaymentPermission;
import com.cmsz.framework.util.JsonUtil;
import com.cmsz.framework.util.KeyValueUtil;
import com.cmsz.framework.util.SerialNumber;
import com.fasterxml.jackson.core.JsonProcessingException;

/**
 * 接收用户下单请求
 * @author Eshin
 *
 */

@Controller
@RequestMapping("/cashier")
public class CashierOrderController {
	
	private static LogHandler logger = LogHandler.getLogger(CashierOrderController.class);
	@Resource(name="BankSecurityRemoting")
	private BetterRemoteService betterRemoteService;
	@Autowired
	private CacheAccessable cacheAccessable;
	
	@Value("${notifyUrl}")
	private String notifyUrl;
	@Value("${cashier.weixin.redirect.url}")
	private String redirectUrl;
	@Value("${weixin.auth.url}")
	private String authUrl;
	
	@ResponseBody
	@RequestMapping(value = "/order",produces = "text/html; charset=utf-8", method = { RequestMethod.POST,
			RequestMethod.GET })
	public ModelAndView order(HttpServletRequest req,SitePreference sitePreference) throws IOException {
		
		ModelAndView modelAndView = new ModelAndView();
		String userAgent = req.getHeader( "USER-AGENT" ).toLowerCase(); 
		String clientIp = getClientID(req);
		CashierOrderInoBean orderBean = null;
//		1、接收到客户端请求
	    try {
			Map<String, String> reqMap = comvertMap(req.getParameterMap());
			orderBean  = initOrderBean(reqMap,clientIp,userAgent);
			logger.info("收银台|CashierOrderController|接收到用户客户端请求",orderBean);
			logger.messageReceive("收银台|CashierOrderController|接收到用户客户端请求："+reqMap.toString());
			
//		    2、订单参数校验
			validateData(reqMap);
			logger.info("收银台|CashierOrderController|参数校验完成",orderBean);
			
//		    3、请求验签
			checkSign(reqMap);
			logger.info("收银台|CashierOrderController|验签完成",orderBean);
			
//		    4、识别到客户端类型
			String clientType = getClientType(sitePreference,userAgent);
			orderBean.setClientType(clientType);
			logger.info("收银台|CashierOrderController|识别到客户端类型为："+clientType,orderBean);
			
//	        5、组装实体bean并签名
			combineBean(orderBean,reqMap);
			logger.info("收银台|CashierOrderController|组装实体bean并签名完成",orderBean);
			
//		    6、查找redis缓存判断该机构可使用支付方式
			setPaymentTpyeList(orderBean);
			logger.info("收银台|CashierOrderController|查询可用支付方式完成",orderBean);
			
//		    7、向用户响应支付方式选择页面
			modelAndView.addObject("orderBean", orderBean);
//			modelAndView.addObject("appid", weixinAppId);
//			modelAndView.addObject("redirecturl", redirectUrl);
			modelAndView.setViewName(CommonConstants.PAY_PAGE);
			if(CommonConstants.CLIENT_WEIXIN.equals(orderBean.getClientType())){
				orderBean.setPaymentType("WEIXIN-JSAPI");
				if(StringUtils.isBlank(orderBean.getWeixinOpenId())){
					modelAndView.addObject("authUrl", combineAuthUrl(orderBean));
					modelAndView.setViewName(CommonConstants.WX_JSAPI_AUTH);
					logger.info("收银台|CashierOrderController|设置微信授权页面参数完成",orderBean);
					logger.messageSend("收银台|CashierOrderController|设置微信授权页面参数完成："+orderBean.toString());
				}
			}
			logger.info("收银台|CashierOrderController|设置页面参数完成",orderBean);
			logger.messageSend("收银台|CashierOrderController|设置页面参数完成："+orderBean.toString());
			
			return modelAndView;
		} catch (Exception e) {
			modelAndView.addObject("orderBean", orderBean);
			logger.error("收银台|CashierOrderController|订单提交失败"+e,orderBean,e);
			modelAndView.setViewName(CommonConstants.ERROR_PAGE);
			return modelAndView;
		}
		
	}
	
	
	/**
	 * 组装授权获取openidURl
	 * @param orderBean
	 * @return
	 * @throws UnsupportedEncodingException 
	 * @throws JsonProcessingException 
	 */
	private String combineAuthUrl(CashierOrderInoBean orderBean) throws UnsupportedEncodingException, JsonProcessingException {
		String state  = "jsondata";
		String orderJson = JsonUtil.object2JsonStr(orderBean);
		/**
		 * base64 解决微信不重定向回来的问题
		 * orderXmlBase64的URL编码：由于发送到微信时被解码了一次，所以重定向时的链接上的参数仍需要URL编码，
		 * 否则从客户浏览器重定向过来的数据会URL编码的问题，因此重定向时URL不需要两次解码，附带的参数需要
		 */
		String orderJsonBase64 = new String(Base64.getEncoder().encode(orderJson.getBytes("UTF-8")));
        String orderJsonBase64UrlEncode = URLEncoder.encode(orderJsonBase64,"utf-8");
		String dataUrl = redirectUrl+"?orderdata="+orderJsonBase64UrlEncode;
		String redirectUrlEncode = URLEncoder.encode(dataUrl,"utf-8");
		String stateUrlEncode = URLEncoder.encode(state,"utf-8");
		String authforOpenIdUrl = authUrl+"?appid="+orderBean.getWeixinAppId()+"&redirect_uri="+redirectUrlEncode+
				"&response_type=code&scope=snsapi_base&state="+stateUrlEncode+"#wechat_redirect";
		return authforOpenIdUrl;
	}

	/**
	 * 设置该终端类型可用支付方式
	 * @param paymentTpyeList
	 * @param orderBean
	 */
	private void setPaymentTpyeList(CashierOrderInoBean orderBean) {
		List<String>paymentTpyeList = new ArrayList<>();
//		查找缓存中可用支付方式
		List<UpayOrgPaymentPermission>paymentPermissionList = cacheAccessable.gainPaymentPermissionList(orderBean.getReqSys());
		if(null == paymentPermissionList || 0 == paymentPermissionList.size()){
			throw new UpayBusinessException("该商户|机构无可用支付方式", null);
		}
		for(UpayOrgPaymentPermission paymentPermission:paymentPermissionList){
			paymentTpyeList.add(paymentPermission.getPayment_type());
		}
//      根据终端类型求交集
		if(CommonConstants.CLIENT_PC.equals(orderBean.getClientType())){
			paymentTpyeList.retainAll(PaymentTypesConstant.getPaymentTpyeInPC());
		}
		if(CommonConstants.CLIENT_MOBILE.equals(orderBean.getClientType())||CommonConstants.CLIENT_TABLET.equals(orderBean.getClientType())){
			paymentTpyeList.retainAll(PaymentTypesConstant.getPaymentTpyeInMobile());
		}
		if(CommonConstants.CLIENT_WEIXIN.equals(orderBean.getClientType())){
			paymentTpyeList.retainAll(PaymentTypesConstant.getPaymentTpyeInWeixin());
		}
		orderBean.setPayTypeList(paymentTpyeList);
	}


	/**
	 * 获取终端IP
	 * @param request
	 * @return
	 */
	private String getClientID(HttpServletRequest request) {
	        String ip = request.getHeader("x-forwarded-for");  
	        if (ip == null || ip.length() == 0 || ip.equalsIgnoreCase("unknown")) {  
	            ip = request.getHeader("Proxy-Client-IP");  
	        }  
	        if (ip == null || ip.length() == 0 || ip.equalsIgnoreCase("unknown")) {  
	            ip = request.getHeader("WL-Proxy-Client-IP");  
	        }  
	        if (ip == null || ip.length() == 0 || ip.equalsIgnoreCase("unknown")) {  
	            ip = request.getRemoteAddr();  
	        }  
	        
	      //对于通过多个代理的情况，第一个IP为客户端真实IP,多个IP按照','分割  
	        if(ip != null && ip.length() > 15){ //"***.***.***.***".length() = 15  
	            if(ip.indexOf(",") > 0){
	            	ip = ip.substring(0,ip.indexOf(","));
	            }
	        }
	        return ip;  
	}


	/**
	 * 组装bean中必要参数
	 * @param orderBean
	 * @param reqMap
	 */
	private void combineBean(CashierOrderInoBean orderBean, Map<String, String> reqMap) {
		orderBean.setActivityCode(reqMap.get("ActivityCode"));
		orderBean.setBuyerId(reqMap.get("BuyerID"));
		orderBean.setIdType(reqMap.get("IDType"));
		orderBean.setIdValue(reqMap.get("IDValue"));
		orderBean.setOrderMoney(Integer.parseInt(reqMap.get("OrderMoney")));
		orderBean.setPayment(Integer.parseInt(reqMap.get("Payment")));
		orderBean.setGift(Integer.parseInt(reqMap.get("Gift")));
		orderBean.setMerActivityId(reqMap.get("MerActivityID"));
		orderBean.setPaymentLimit(reqMap.get("PaymentLimit"));
		orderBean.setProductId(reqMap.get("ProductID"));
		orderBean.setProductName(reqMap.get("ProductName"));
		orderBean.setProductDesc(reqMap.get("ProductDesc"));
		orderBean.setProductUrl(reqMap.get("ProductURL"));
		orderBean.setReturnUrl(reqMap.get("ReturnURL"));
//		orderBean.setClientIp(reqMap.get("ClientIP"));
		orderBean.setCustomParam(reqMap.get("CustomParam"));
		orderBean.setWeixinAppId(reqMap.get("WeiXinAppId"));
		orderBean.setWeixinOpenId(reqMap.get("WeiXinOpenId"));
		if (StringUtils.isBlank(reqMap.get("NotifyURL"))) {
			reqMap.put("NotifyURL", notifyUrl);
			orderBean.setNotifyUrl(notifyUrl);
		}else{
			orderBean.setNotifyUrl(reqMap.get("NotifyURL"));
		}
//		为选择支付方式前的bean内容签名
		signOrderBean(orderBean);
	}

	/**
	 * 为orderBean的数据签名，在用户选择支付方式提交后验签
	 * 防止用户修改数据或伪造页面
	 * @param orderBean
	 */
	private void signOrderBean(CashierOrderInoBean orderBean) {
//		与返回到页面显示的数据对应，不包含选择支付方式
		String orderValueStr = ParamUtil.genPageParamSignStr(orderBean);
		
		try {
			String signStr = betterRemoteService.sign(null, orderValueStr);
			SignResult signResult = JsonUtil.jsonStr2Obj(signStr, SignResult.class);
			orderBean.setCerID(signResult.getCerId());
			orderBean.setSignValue(signResult.getSignValue());
		} catch (Exception e) {
			throw new RuntimeException("orderBean签名失败",e);
		}
	}


	/**
	 * 初始化orderbean
	 * @param reqMap
	 * @param userAgent 
	 * @param clientIp 
	 * @return
	 */
	private CashierOrderInoBean initOrderBean(Map<String, String> reqMap, String clientIp, String userAgent) {
		CashierOrderInoBean orderBean = new CashierOrderInoBean();
		String orderNo = reqMap.get("OrderNo");
		orderBean.setOrderNo(orderNo);
		orderBean.setReqSys(orderNo.substring(0, 4));
		orderBean.setReqDate(orderNo.substring(4, 12));
		orderBean.setReqTransId("0001" + SerialNumber.generateRandomSerial(28));
		orderBean.setReqDateTime(orderNo.substring(4, 18));
		
		orderBean.setUserAgent(userAgent);
		orderBean.setClientIp(clientIp);
		return orderBean;
	}

	private String getClientType(SitePreference sitePreference, String userAgent) {
	     if(userAgent.toLowerCase().contains(CommonConstants.KEYWORD_WEIXIN)){
	    	 return CommonConstants.CLIENT_WEIXIN;
	     }
		 if (sitePreference == SitePreference.NORMAL) {
		    	return CommonConstants.CLIENT_PC;
	        } else if (sitePreference == SitePreference.MOBILE) {
	        	return CommonConstants.CLIENT_MOBILE;
	        } else if (sitePreference == SitePreference.TABLET) {
	        	return CommonConstants.CLIENT_TABLET;
	        } else {
	        	return CommonConstants.CLIENT_PC;
	        }
		
	}

	/**
	 * 将http请求参数转换成Map<String, String>
	 * @param map
	 * @return
	 */
	private Map<String, String> comvertMap(Map<String, String[]> map) {
		Map<String, String> newMap = new HashMap<String, String>();
		Iterator<Entry<String, String[]>> it = map.entrySet().iterator();
		while (it.hasNext()) {
			Entry<String, String[]> entry = it.next();
			String key = entry.getKey();
			String[] value = entry.getValue();
			newMap.put(key, value[0]);
		}
		return newMap;
	}
	
	/**
	 * 对商户前置的签名进行验签
	 * @param reqMap
	 */
	private void checkSign(Map<String, String> reqMap) {
		String cerId = reqMap.remove("CerID");
		String signature = reqMap.remove("SignValue");
		try {
			String plainText = KeyValueUtil.mapToString(reqMap);
			if (!betterRemoteService.verify(cerId, plainText, signature)) {
				throw new RuntimeException("验签失败");
			}
		} catch (Exception e) {
			throw new RuntimeException("验签失败");
		}
	}
	
	/**
	 * 校验商户请求数据
	 * @param reqMap
	 */
	private void validateData(Map<String, String> reqMap) {
		Map<String, String> checkMap = new HashMap<>();
		checkMap.putAll(reqMap);
		String orderNo = checkMap.remove("OrderNo");
		checkOrderNo(orderNo);
		checkField(checkMap, "ActivityCode", true, true, 32);
		checkField(checkMap, "BuyerID", false, true, 32);
		checkField(checkMap, "IDType", false, false, 2);
		checkField(checkMap, "IDValue", false, true, 32);
		checkField(checkMap, "OrderMoney", true, true, 9);
		checkField(checkMap, "Payment", true, true, 9);
		checkField(checkMap, "Gift", true, true, 9);
		checkField(checkMap, "MerActivityID", false, true, 32);
//		checkField(checkMap, "PaymentType", true, true, 32);
		checkField(checkMap, "PaymentLimit", false, true, 32);
		checkField(checkMap, "ProductID", true, true, 32);
		checkField(checkMap, "ProductName", true, true, 32);
		checkField(checkMap, "ProductDesc", true, true, 32);
		checkField(checkMap, "ProductURL", false, true, -1);
		checkField(checkMap, "NotifyURL", true, true, -1);
		checkField(checkMap, "ReturnURL", false, true, -1);
//		checkField(checkMap, "DefaultBank", false, true, -1);
//		checkField(checkMap, "ClientIP", false, true, 15);
		checkField(checkMap, "CustomParam", false, true, 1024);
		checkField(checkMap, "WeiXinAppId", false, true, 32);
		checkField(checkMap, "WeiXinOpenId", false, true, 128);
		checkField(checkMap, "CerID", true, true, 32);
		checkField(checkMap, "SignValue", true, true, 2048);
		for (Map.Entry<String, String> entry : checkMap.entrySet()) {
			throw new IllegalArgumentException("非法参数：" + entry.getKey());
		}
		//对收银台的交易编码U104_001进行强校验
		String activityCode = reqMap.get("ActivityCode");
		checkActivityCode(activityCode);
	}
	
	/**
	 * 
	 * TODO(对收银台的交易编码U104_001进行强校验)  
	 * @param activityCode 
	 * @author Edison_lv  
	 * @Date 2017年3月1日下午4:54:27
	 */
	private void checkActivityCode(String activityCode){
		if(!"U104_001".equals(activityCode)){
			throw new IllegalArgumentException("非法参数：" + activityCode);
		}
	}
	
	/**
	 * 从Map中移除元素进行校验，检查是否必填，长度是否正确
	 * @param checkMap
	 * @param fieldName
	 * @param must
	 * @param variableLength
	 * @param maxLength
	 */
	private void checkField(Map<String, String> checkMap, String fieldName, boolean must, boolean variableLength, int maxLength) {
		String field = checkMap.remove(fieldName);
		if (StringUtils.isBlank(field)) {
			if (must) {
				throw new IllegalArgumentException(fieldName + "属性必填，缺没有值");
			} else{
				return;
			}
		}
		// -1表示无长度限制
		if (maxLength == -1) {
			return;
		}
		if (field.length() > maxLength) {
			throw new IllegalArgumentException(fieldName + "长度超过限制");
		}
		if (!variableLength && field.length() != maxLength) {
			throw new IllegalArgumentException(fieldName + "长度不正确");
		}
	}
	
	/**
	 * 校验订单号
	 * @param orderNo
	 */
	private void checkOrderNo(String orderNo) {
		if (StringUtils.isNotBlank(orderNo) && orderNo.length() >= 18) {
			try {
				new SimpleDateFormat("yyyyMMddHHmmss").parse(orderNo.substring(4, 18));
				return;
			} catch (ParseException e) {
				throw new IllegalArgumentException("OrderNo属性值5到18为须是时间格式");
			}
		}
		throw new IllegalArgumentException("OrderNo属性值长度非法");
	}
}
